﻿using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
using YJKArch.Utils;

namespace RegionSplittor.Utils
{
	public class ElementSelector
	{
		public static List<Element> GetLoopsElement(Autodesk.Revit.DB.Document Linkdoc, List<CurveLoop> arloop, XYZ extrusionDir, double extrusionDist)
		{
			return ElementSelector.GetElementByCurveLoop(Linkdoc, arloop, extrusionDir, extrusionDist);
		}

		public static List<Element> GetSolidIntersectElement(Autodesk.Revit.DB.Document doc, List<CurveLoop> lstLoop, XYZ extrusionDir, double extrusionDist, ElementMulticategoryFilter categoryFilter = null)
		{
			List<Element> result = new List<Element>();
			if (lstLoop.Count < 1 || lstLoop == null)
			{
				return result;
			}
			try
			{
				Solid solid;
				if (!GeometryCreateUtils.CreateSolid(lstLoop, extrusionDir, extrusionDist, out solid))
				{
					return result;
				}
				ElementIntersectsSolidFilter elementIntersectsSolidFilter = new ElementIntersectsSolidFilter(solid);
				FilteredElementCollector filteredElementCollector = new FilteredElementCollector(doc);
				if (categoryFilter == null)
				{
					filteredElementCollector.WherePasses(elementIntersectsSolidFilter);
				}
				else
				{
					LogicalAndFilter logicalAndFilter = new LogicalAndFilter(elementIntersectsSolidFilter, categoryFilter);
					filteredElementCollector.WherePasses(logicalAndFilter);
				}
				result = filteredElementCollector.WhereElementIsNotElementType().ToList<Element>();
				solid.Dispose();
				filteredElementCollector.Dispose();
				elementIntersectsSolidFilter.Dispose();
				GC.Collect();
			}
			catch (Exception)
			{
			}
			return result;
		}

		public static List<Element> GetViewElement(Autodesk.Revit.DB.Document doc, double dElevation, XYZ extrusionDir)
		{
			List<Element> result = new List<Element>();
			try
			{
				Autodesk.Revit.DB.View viewPlanByElevation = ElementSelector.GetViewPlanByElevation(doc, dElevation);
				if (viewPlanByElevation == null)
				{
					return result;
				}
				Level genLevel = viewPlanByElevation.GenLevel;
				List<Element> sameLevelElements = ElementSelector.GetSameLevelElements(doc, genLevel);
				double columnHeight = ElementSelector.GetColumnHeight(sameLevelElements);
				Outline elementsOutLine = ElementSelector.GetElementsOutLine(sameLevelElements, viewPlanByElevation);
				XYZ minimumPoint = elementsOutLine.MinimumPoint;
				XYZ maximumPoint = elementsOutLine.MaximumPoint;
				ElementSelector.GetCurveLoopByMinMax(minimumPoint, maximumPoint, genLevel.Elevation);
				List<CurveLoop> arCurveLoop = new List<CurveLoop>();
				result = ElementSelector.GetElementByCurveLoop(doc, arCurveLoop, extrusionDir, columnHeight);
			}
			catch
			{
			}
			return result;
		}

		public static List<Element> GetElementsByType(Autodesk.Revit.DB.Document doc, Type classType)
		{
			return new FilteredElementCollector(doc).OfClass(classType).ToElements().ToList<Element>();
		}

		public static List<Element> GetElementByCategory(List<Element> lstEle, List<BuiltInCategory> lstCategory)
		{
			List<Element> list = new List<Element>();
			foreach (Element element in lstEle)
			{
				BuiltInCategory integerValue = (Autodesk.Revit.DB.BuiltInCategory)(element.Category.Id.IntegerValue);
				if (lstCategory.Contains(integerValue))
				{
					list.Add(element);
				}
			}
			return lstEle;
		}

		public static List<Element> GetElementByCategory(List<Element> lstEle, BuiltInCategory cat)
		{
			List<Element> list = new List<Element>();
			foreach (Element element in lstEle)
			{
				BuiltInCategory integerValue = (Autodesk.Revit.DB.BuiltInCategory)(element.Category.Id.IntegerValue);
				if (cat.Equals(integerValue))
				{
					list.Add(element);
				}
			}
			return lstEle;
		}

		private static List<Element> GetElementByCurveLoop(Autodesk.Revit.DB.Document doc, List<CurveLoop> arCurveLoop, XYZ extrusionDir, double extrusionDist)
		{
			List<Element> list = new List<Element>();
			if (arCurveLoop.Count < 1 || arCurveLoop == null)
			{
				return list;
			}
			Solid solid = null;
			try
			{
				solid = GeometryCreationUtilities.CreateExtrusionGeometry(arCurveLoop, extrusionDir, extrusionDist);
			}
			catch (Exception)
			{
			}
			new ElementCategoryFilter((Autodesk.Revit.DB.BuiltInCategory)(-1999010));
			ElementFilter elementFilter = new ElementCategoryFilter(BuiltInCategory.OST_StructuralFraming);
			ElementClassFilter elementClassFilter = new ElementClassFilter(typeof(Wall));
			ElementIntersectsSolidFilter elementIntersectsSolidFilter = new ElementIntersectsSolidFilter(solid);
			LogicalAndFilter logicalAndFilter = new LogicalAndFilter(new LogicalOrFilter(elementFilter, elementClassFilter), elementIntersectsSolidFilter);
			foreach (Element element in new FilteredElementCollector(doc).WherePasses(logicalAndFilter).ToElements().ToList<Element>())
			{
				if (element is FamilyInstance || element is Wall)
				{
					list.Add(element);
				}
			}
			return list;
		}

		private static Outline GetElementsOutLine(List<Element> arEle, Autodesk.Revit.DB.View view)
		{
			Outline outline = new Outline(XYZ.Zero, XYZ.Zero);
			foreach (Element element in arEle)
			{
				BoundingBoxXYZ boundingBoxXYZ = element.get_BoundingBox(view);
				outline.AddPoint(boundingBoxXYZ.Min);
				outline.AddPoint(boundingBoxXYZ.Max);
			}
			return outline;
		}

		private static double GetColumnHeight(List<Element> arEle)
		{
			double result = 0.0;
			foreach (Element ele in arEle)
			{
				if (BeamOperator.IsColumn(ele))
				{
					result = BeamOperator.GetEleParamDoubleValue(ele, "长度");
					break;
				}
			}
			return result;
		}

		private static CurveLoop GetCurveLoopByMinMax(XYZ ptMin, XYZ ptMax, double dZValue)
		{
			CurveLoop curveLoop = new CurveLoop();
			XYZ xyz = new XYZ(ptMin.X, ptMin.Y, dZValue);
			XYZ xyz2 = new XYZ(ptMin.X, ptMax.Y, dZValue);
			XYZ xyz3 = new XYZ(ptMax.X, ptMax.Y, dZValue);
			XYZ xyz4 = new XYZ(ptMax.X, ptMin.Y, dZValue);
			curveLoop.Append(Line.CreateBound(xyz, xyz2));
			curveLoop.Append(Line.CreateBound(xyz2, xyz3));
			curveLoop.Append(Line.CreateBound(xyz3, xyz4));
			curveLoop.Append(Line.CreateBound(xyz4, xyz));
			return curveLoop;
		}

		public static Autodesk.Revit.DB.View GetViewPlanByElevation(Autodesk.Revit.DB.Document doc, double dElevation)
		{
			Autodesk.Revit.DB.View result = null;
			foreach (ViewPlan viewPlan in new FilteredElementCollector(doc).OfClass(typeof(ViewPlan)).Cast<ViewPlan>().ToList<ViewPlan>())
			{
				if (viewPlan.GenLevel != null && viewPlan.GenLevel.IsValidObject && Math.Abs(viewPlan.GenLevel.ProjectElevation - dElevation) < ElementSelector.m_dLevelEsp)
				{
					result = viewPlan;
					break;
				}
			}
			return result;
		}

		public static Level GetLevelByElevation(Autodesk.Revit.DB.Document doc, double dElevation)
		{
			Level result = null;
			List<Level> list = new FilteredElementCollector(doc).OfClass(typeof(Level)).Cast<Level>().ToList<Level>();
			SortedDictionary<double, Level> sortedDictionary = new SortedDictionary<double, Level>();
			foreach (Level level in list)
			{
				if (dElevation - 0.005 <= level.Elevation)
				{
					double key = Math.Abs(level.Elevation - dElevation);
					sortedDictionary.Add(key, level);
				}
			}
			if (sortedDictionary.Count > 0)
			{
				result = sortedDictionary.First<KeyValuePair<double, Level>>().Value;
			}
			return result;
		}

		public static Level GetUpLevelByElevation(Autodesk.Revit.DB.Document doc, double dElevation)
		{
			Level result = null;
			List<Level> list = new FilteredElementCollector(doc).OfClass(typeof(Level)).Cast<Level>().ToList<Level>();
			SortedDictionary<double, Level> sortedDictionary = new SortedDictionary<double, Level>();
			foreach (Level level in list)
			{
				if (level.Elevation > dElevation)
				{
					double key = Math.Abs(level.Elevation - dElevation);
					if (!sortedDictionary.ContainsKey(key))
					{
						sortedDictionary.Add(key, level);
					}
				}
			}
			if (sortedDictionary.Count > 0)
			{
				result = sortedDictionary.First<KeyValuePair<double, Level>>().Value;
			}
			return result;
		}

		private static List<Element> GetSameLevelElements(Autodesk.Revit.DB.Document doc, Level level)
		{
			List<Element> list = new List<Element>();
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(doc);
			filteredElementCollector.WhereElementIsNotElementType();
			foreach (Element element in filteredElementCollector.ToElements().ToList<Element>())
			{
				if (element.LevelId == level.Id)
				{
					list.Add(element);
				}
			}
			return list;
		}

		public static double m_dLevelEsp = Common.MMtoIntch(200.0);
	}
}
